﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;

namespace STEditor.Classes
{
    public class STGameXamlWriter
    {

        public static string TilesFolderName = "Tiles";
        public static string GameDataFileName = "GameData.xaml";
        public static string AssemblyName = "SilverTileApp";

        public void Save(STGame saveGame, string folder)
        {

            var saveFolder = new DirectoryInfo(folder);

            saveFolder.Create();

            WriteTileImages(saveGame, folder);

            var gameDataFile = new FileInfo(Path.Combine(folder, GameDataFileName));
            if (gameDataFile.Exists)
            {
                gameDataFile.Delete();
            }
            
            using (var gameDataStream = gameDataFile.OpenWrite())
            {
                WriteGameXml(saveGame, gameDataStream);
            }

        }

        private void WriteTileImages(STGame game, string outputFolder)
        {

            var tilesFolder = new DirectoryInfo(Path.Combine(outputFolder, TilesFolderName));
            tilesFolder.Create();
            
            GetUsedTileImages(game)
                .ToList()
                .ForEach(t =>WriteImageAsPng(t, tilesFolder.FullName));

        }

        private void WriteImageAsPng(STTile tile, string targetFolder)
        {

            string newFileName = Path.GetFileNameWithoutExtension(tile.Image.Source.LocalPath) + ".png";
            newFileName = Path.Combine(targetFolder, newFileName);
            
            // Write tile image as png
            var png = new System.Windows.Media.Imaging.PngBitmapEncoder();
            png.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(tile.Image));

            using (var fileOut = File.OpenWrite(newFileName))
            {
                png.Save(fileOut);
            }

            tile.Image = new STFrame { Source = new Uri(@"file://" + newFileName) };

        }

        private IEnumerable<STTile> GetUsedTileImages(STGame game)
        {
            return game
                .GameAreas
                .SelectMany(g => g.Tiles)
                .Distinct()
                .Where(t => t.Image.Source != null);
        }

        private void WriteGameXml(STGame game, Stream gameDataStrm)
        {

            var gameDataWriter = new XmlTextWriter(gameDataStrm, Encoding.UTF8);
            gameDataWriter.Formatting = Formatting.Indented;

            gameDataWriter.WriteStartElement("ResourceDictionary");
            gameDataWriter.WriteAttributeString("xmlns", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
            gameDataWriter.WriteAttributeString("xmlns:x", "http://schemas.microsoft.com/winfx/2006/xaml");
            gameDataWriter.WriteAttributeString("xmlns:st", "clr-namespace:SilverTile.Xaml;assembly=SilverTile");

            // Write SpriteFrame elements for all used sprites
            GetUsedTileImages(game)
                .Select(t => t.Image.Source)
                .Distinct()
                .ToList()
                .ForEach(u =>
                    {

                        gameDataWriter.WriteStartElement("st:SpriteFrame");

                        gameDataWriter.WriteAttributeString("x:Key", Path.GetFileNameWithoutExtension(u.LocalPath));
                        gameDataWriter.WriteAttributeString("Source", GenerateTileUri(u));

                        gameDataWriter.WriteEndElement();

                    });

            game
                .GameAreas
                .ToList()
                .ForEach(a => WriteGameAreaXml(a, gameDataWriter));

            gameDataWriter.WriteEndElement();

            gameDataWriter.Close();

        }

        private void WriteGameAreaXml(STGameArea gameArea, XmlTextWriter writer)
        {

            writer.WriteStartElement("st:GameAreaGrid");

            writer.WriteAttributeString("x:Key", gameArea.AreaName);
            writer.WriteAttributeString("AreaName", gameArea.AreaName);
            writer.WriteAttributeString("Columns", gameArea.Width.ToString());
            writer.WriteAttributeString("Rows", gameArea.Height.ToString());
            writer.WriteAttributeString("TileWidth", gameArea.TileWidth.ToString());
            writer.WriteAttributeString("TileHeight", gameArea.TileHeight.ToString());

            gameArea
                .Tiles
                .OrderBy(t => t.X)
                .OrderBy(t => t.Y)
                .ToList()
                .ForEach(t =>
                {
                    WriteTileXml(t, writer);
                });

            writer.WriteEndElement();

        }

        private void WriteTileXml(STTile tile, XmlTextWriter writer)
        {

            writer.WriteStartElement("st:SpriteTile");
            writer.WriteAttributeString("X", tile.Column.ToString());
            writer.WriteAttributeString("Y", tile.Row.ToString());
            //writer.WriteAttributeString("Blocking", tile.Blocking.ToString());
            writer.WriteAttributeString("Frame", GenerateTileResource(tile.Image.Source));
            if (tile.Transition != null)
            {
                WriteTransitionXml(tile.Transition, writer);
            }
            
            writer.WriteEndElement();
            
        }

        private void WriteTransitionXml(STAreaTransition transition, XmlTextWriter writer)
        {
            
            writer.WriteStartElement("st:SpriteTile.Transition");
            writer.WriteStartElement("st:AreaTransition");
            writer.WriteAttributeString("TargetColumn", transition.TargetColumn.ToString());
            writer.WriteAttributeString("TargetRow", transition.TargetRow.ToString());
            writer.WriteAttributeString("GameAreaName", transition.GameAreaName);
            writer.WriteAttributeString("CollisionSides", transition.CollisionSides);
            writer.WriteEndElement();
            writer.WriteEndElement();

        }

        private string GenerateTileUri(Uri sourceUri)
        {
            string fileName = Path.GetFileName(sourceUri.LocalPath);
            string uri = string.Format("/{0};component/{1}/{2}", AssemblyName, TilesFolderName, fileName);
            return uri;
        }

        private string GenerateTileResource(Uri sourceUri)
        {
            if (sourceUri == null)
            {
                return "{x:Null}";
            }
            else
            {
                return "{StaticResource " + Path.GetFileNameWithoutExtension(sourceUri.LocalPath) + "}";
            }            
        }

    }
}
